#ifndef PTR_H
#define PTR_H

#include <cstddef>
#include <new>

template<class T> class Ptr {
private:
	T *pointedElement;
	int *referenceCounter;

public:
	Ptr();

	explicit Ptr(T* pointer);

	Ptr(const Ptr<T> &pointer);

	~Ptr();

	operator T*();

	operator T*() const;

	T& operator *();

	const T& operator *() const;

	T** operator &();

	const T** operator &() const;

	T* operator ->();

	const T* operator ->() const;

	bool operator ==(const T *pointer) const;

	bool operator ==(const Ptr<T> &pointer) const;

	Ptr<T>& operator =(T *pointer);

	Ptr<T>& operator =(const Ptr<T> &pointer);

	bool isNull() const;

	std::size_t size() const;

	std::size_t realSize() const;

	void release();
};

template<class T>
Ptr<T>::Ptr() : pointedElement(NULL), referenceCounter(NULL) {
}

template<class T>
Ptr<T>::Ptr(T* pointer) : pointedElement(NULL), referenceCounter(NULL) {
	if(pointer != NULL) {
		this->pointedElement = pointer;
		this->referenceCounter = new int(1);
	}
}

template<class T>
Ptr<T>::Ptr(const Ptr<T> &pointer) : pointedElement(NULL), referenceCounter(NULL) {
	*this = pointer;
}

template<class T>
Ptr<T>::~Ptr() {
	this->release();
}

template<class T>
inline Ptr<T>::operator T*() {
	return this->pointedElement;
}

template<class T>
inline Ptr<T>::operator T*() const {
	return this->pointedElement;
}

template<class T>
inline T& Ptr<T>::operator *() {
	return *this->pointedElement;
}

template<class T>
inline const T& Ptr<T>::operator *() const {
	return *this->pointedElement;
}

template<class T>
inline T** Ptr<T>::operator &() {
	return &this->pointedElement;
}

template<class T>
inline const T** Ptr<T>::operator &() const {
	return &this->pointedElement;
}

template<class T>
inline T* Ptr<T>::operator ->() {
	return this->pointedElement;
}

template<class T>
inline const T* Ptr<T>::operator ->() const {
	return this->pointedElement;
}

template<class T>
inline bool Ptr<T>::operator ==(const T *pointer) const {
	return this->pointedElement == pointer;
}

template<class T>
inline bool Ptr<T>::operator ==(const Ptr<T> &pointer) const {
	return this->pointedElement == pointer.pointedElement;
}

template<class T>
inline Ptr<T>& Ptr<T>::operator =(T *pointer) {
	return (*this = Ptr<T>(pointer));
}

template<class T>
inline Ptr<T>& Ptr<T>::operator =(const Ptr<T> &pointer) {
	if(this->pointedElement != pointer.pointedElement) {
		this->release();
		this->pointedElement = pointer.pointedElement;
		this->referenceCounter = pointer.referenceCounter;
		if(this->referenceCounter != NULL) {
			(*this->referenceCounter)++;
		}
	}
	return *this;
}

template<class T>
inline bool Ptr<T>::isNull() const {
	return this->pointedElement == NULL;
}

template<class T>
inline size_t Ptr<T>::size() const {
	return sizeof(T);
}

template<class T>
inline size_t Ptr<T>::realSize() const {
	return sizeof(this->pointedElement) + sizeof(T) +
		   sizeof(this->referenceCounter) + sizeof(int);
}

template<class T>
inline void Ptr<T>::release() {
	if(this->referenceCounter != NULL) {
		if(--(*this->referenceCounter) <= 0) {
			if(this->pointedElement != NULL) {
				delete this->pointedElement;
			}
			delete this->referenceCounter;
		}
		this->pointedElement = NULL;
		this->referenceCounter = NULL;
	}
}

#endif
